﻿using System;
using System.Management.Automation.Runspaces;
using System.Net;
using System.Security;
using System.Workflow.ComponentModel;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.WorkflowActions;
using Nintex.Workflow;


namespace NintexCustomWFActivities
{
    public partial class ExecutePowerShellActivity : Nintex.Workflow.Activities.ProgressTrackingActivity
    {
        //static string LoggingAreaCategory = LoggingConstants.LOGGING_AREA + "/" + LoggingConstants.LOGGING_AREA_CATEGORY_ACTIVITY;

        public ExecutePowerShellActivity()
        {
            InitializeComponent();
        }

        public WorkflowContext __Context
        {
            get { return (WorkflowContext)GetValue(__ContextProperty); }
            set { SetValue(__ContextProperty, value); }
        }

        public static readonly DependencyProperty __ContextProperty =
            DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(ExecutePowerShellActivity));


        public string __ListId
        {
            get { return (string)GetValue(__ListIdProperty); }
            set { SetValue(__ListIdProperty, value); }
        }

        public static readonly DependencyProperty __ListIdProperty =
            DependencyProperty.Register("__ListId", typeof(string), typeof(ExecutePowerShellActivity));


        public SPItemKey __ListItem
        {
            get { return (SPItemKey)base.GetValue(__ListItemProperty); }
            set { base.SetValue(__ContextProperty, value); }
        }

        public static readonly DependencyProperty __ListItemProperty =
            DependencyProperty.Register("__ListItem", typeof(SPItemKey), typeof(ExecutePowerShellActivity));

        public string Script
        {
            get { return (string)GetValue(ScriptProperty); }
            set { SetValue(ScriptProperty, value); }
        }

        public static readonly DependencyProperty ScriptProperty =
            DependencyProperty.Register("Script", typeof(string), typeof(ExecutePowerShellActivity));


        public string Signature
        {
            get { return (string)GetValue(SignatureProperty); }
            set { SetValue(SignatureProperty, value); }
        }

        public static readonly DependencyProperty SignatureProperty =
            DependencyProperty.Register("Signature", typeof(string), typeof(ExecutePowerShellActivity));

        public string Var1
        {
            get { return (string)GetValue(Var1Property); }
            set { SetValue(Var1Property, value); }
        }

        public static readonly DependencyProperty Var1Property =
            DependencyProperty.Register("Var1", typeof(string), typeof(ExecutePowerShellActivity));

        public string Var2
        {
            get { return (string)GetValue(Var2Property); }
            set { SetValue(Var2Property, value); }
        }

        public static readonly DependencyProperty Var2Property =
            DependencyProperty.Register("Var2", typeof(string), typeof(ExecutePowerShellActivity));

        public string Var3
        {
            get { return (string)GetValue(Var3Property); }
            set { SetValue(Var3Property, value); }
        }

        public static readonly DependencyProperty Var3Property =
            DependencyProperty.Register("Var3", typeof(string), typeof(ExecutePowerShellActivity));

        public string Var4
        {
            get { return (string)GetValue(Var4Property); }
            set { SetValue(Var4Property, value); }
        }

        public static readonly DependencyProperty Var4Property =
            DependencyProperty.Register("Var4", typeof(string), typeof(ExecutePowerShellActivity));

        public string Var5
        {
            get { return (string)GetValue(Var5Property); }
            set { SetValue(Var5Property, value); }
        }

        public static readonly DependencyProperty Var5Property =
            DependencyProperty.Register("Var5", typeof(string), typeof(ExecutePowerShellActivity));

        public string Secure
        {
            get { return (string)GetValue(SecureProperty); }
            set { SetValue(SecureProperty, value); }
        }

        public static readonly DependencyProperty SecureProperty =
            DependencyProperty.Register("Secure", typeof(string), typeof(ExecutePowerShellActivity));

        public string SecureStoreAppId
        {
            get { return (string)GetValue(SecureStoreAppIdProperty); }
            set { SetValue(SecureStoreAppIdProperty, value); }
        }

        public static readonly DependencyProperty SecureStoreAppIdProperty =
           DependencyProperty.Register("SecureStoreAppId", typeof(string), typeof(ExecutePowerShellActivity));

        public string Var1Out
        {
            get { return (string)GetValue(Var1OutProperty); }
            set { SetValue(Var1OutProperty, value); }
        }

        public static readonly DependencyProperty Var1OutProperty =
            DependencyProperty.Register("Var1Out", typeof(string), typeof(ExecutePowerShellActivity));

        public string Var2Out
        {
            get { return (string)GetValue(Var2OutProperty); }
            set { SetValue(Var2OutProperty, value); }
        }

        public static readonly DependencyProperty Var2OutProperty =
            DependencyProperty.Register("Var2Out", typeof(string), typeof(ExecutePowerShellActivity));

        public string Var3Out
        {
            get { return (string)GetValue(Var3OutProperty); }
            set { SetValue(Var3OutProperty, value); }
        }

        public static readonly DependencyProperty Var3OutProperty =
            DependencyProperty.Register("Var3Out", typeof(string), typeof(ExecutePowerShellActivity));

        public string Var4Out
        {
            get { return (string)GetValue(Var4OutProperty); }
            set { SetValue(Var4OutProperty, value); }
        }

        public static readonly DependencyProperty Var4OutProperty =
            DependencyProperty.Register("Var4Out", typeof(string), typeof(ExecutePowerShellActivity));

        public string Var5Out
        {
            get { return (string)GetValue(Var5OutProperty); }
            set { SetValue(Var5OutProperty, value); }
        }

        public static readonly DependencyProperty Var5OutProperty =
            DependencyProperty.Register("Var5Out", typeof(string), typeof(ExecutePowerShellActivity));


        private const string var1 = "var1";
        private const string var2 = "var2";
        private const string var3 = "var3";
        private const string var4 = "var4";
        private const string var5 = "var5";
        private const string secure = "secure";
        private const string credential = "credential";

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            try
            {
                //Logger.LogTraceMessage(LoggingConstants.TraceMessage.Begin, LoggingAreaCategory);
                // Throws an exception if the action is not allowed.
                ActivityActivationReference.IsAllowed(this, __Context.Web);
                NWWorkflowContext ctx = NWWorkflowContext.GetContext(
                      this.__Context,
                      new Guid(this.__ListId),
                      this.__ListItem.Id,
                      this.WorkflowInstanceId,
                      this);

                base.LogProgressStart(ctx);

                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    if (IsSigningRequired)
                    {
                        //Logger.LogToDeveloper("Signing is required", (int)EventId.TraceMessage, TraceSeverity.Verbose, LoggingAreaCategory);
                        ValidateSignature(Script, Signature);
                    }

                    using (Runspace runspace = RunspaceFactory.CreateRunspace())
                    {
                        runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;
                        runspace.Open();

                        runspace.SessionStateProxy.SetVariable("ctx", __Context);
                        runspace.SessionStateProxy.SetVariable("sharePointService", (ISharePointService)executionContext.GetService(typeof(ISharePointService)));
                        runspace.SessionStateProxy.SetVariable("listItemService", (IListItemService)executionContext.GetService(typeof(IListItemService)));


                        runspace.SessionStateProxy.SetVariable("site", __Context.Site);
                        runspace.SessionStateProxy.SetVariable("web", __Context.Web);

                        //Logger.LogToDeveloper(string.Format("Site URL={0}, Web Url={1}", __Context.Site.Url, __Context.Web.Url), (int)EventId.TraceMessage, TraceSeverity.Verbose, LoggingAreaCategory);

                        if (__Context.ItemId > 0)
                        {
                            SPList list = __Context.Web.Lists[new Guid(__Context.ListId)];
                            SPListItem item = list.GetItemById(__Context.ItemId);

                            runspace.SessionStateProxy.SetVariable("list", list);
                            runspace.SessionStateProxy.SetVariable("item", item);
                            //Logger.LogToDeveloper(string.Format("List URL={0}, Item ID={1}", list.RootFolder.Url, item.ID), (int)EventId.TraceMessage, TraceSeverity.Verbose, LoggingAreaCategory);
                        }

                        string var1Resolved = ctx.AddContextDataToString(this.Var1);
                        string var2Resolved = ctx.AddContextDataToString(this.Var2);
                        string var3Resolved = ctx.AddContextDataToString(this.Var3);
                        string var4Resolved = ctx.AddContextDataToString(this.Var4);
                        string var5Resolved = ctx.AddContextDataToString(this.Var5);
                        //Logger.LogToDeveloper(string.Format("Input parameters Var1={0}, Var2={1}, Var3={2}, Var4={3}, Var5={4}", var1Resolved, var2Resolved, var3Resolved, var4Resolved, var5Resolved), (int)EventId.TraceMessage, TraceSeverity.Verbose, LoggingAreaCategory);

                        runspace.SessionStateProxy.SetVariable(var1, var1Resolved);
                        runspace.SessionStateProxy.SetVariable(var2, var2Resolved);
                        runspace.SessionStateProxy.SetVariable(var3, var3Resolved);
                        runspace.SessionStateProxy.SetVariable(var4, var4Resolved);
                        runspace.SessionStateProxy.SetVariable(var5, var5Resolved);

                        if (!String.IsNullOrEmpty(Secure))
                        {
                            //Logger.LogToDeveloper("Secure:" + Secure, (int)EventId.TraceMessage, TraceSeverity.Verbose, LoggingAreaCategory);
                            runspace.SessionStateProxy.SetVariable(secure, DecryptString(Secure));
                        }

                        if (!String.IsNullOrEmpty(SecureStoreAppId))
                        {
                            //Logger.LogToDeveloper(string.Format("Using SecureStoreAppId SecureStoreAppId={0}", SecureStoreAppId), (int)EventId.TraceMessage, TraceSeverity.Verbose, LoggingAreaCategory);
                            NetworkCredential cred = Helper.GetSecureStoreCredentials(__Context.Site, SecureStoreAppId);
                            runspace.SessionStateProxy.SetVariable(credential, cred);
                        }

                        Pipeline pipeline = runspace.CreatePipeline();

                        string resolvedScript = Helper.ReplaceTokens(ctx.AddContextDataToString(this.Script), __Context);
                        pipeline.Commands.AddScript(resolvedScript);

                        pipeline.Invoke();

                        Var1Out = runspace.SessionStateProxy.GetVariable(var1) as String;
                        Var2Out = runspace.SessionStateProxy.GetVariable(var2) as String;
                        Var3Out = runspace.SessionStateProxy.GetVariable(var3) as String;
                        Var4Out = runspace.SessionStateProxy.GetVariable(var4) as String;
                        Var5Out = runspace.SessionStateProxy.GetVariable(var5) as String;

                        //Logger.LogToDeveloper(string.Format("Output parameters Var1={0}, Var2={1}, Var3={2}, Var4={3}, Var5={4}", Var1Out, Var2Out, Var3Out, Var4Out, Var5Out), (int)EventId.TraceMessage, TraceSeverity.Verbose, LoggingAreaCategory);
                        runspace.Close();
                    }

                });
                base.LogProgressEnd(ctx, executionContext);
                //Logger.LogTraceMessage(LoggingConstants.TraceMessage.End, LoggingAreaCategory);
                return ActivityExecutionStatus.Closed;
            }
            catch (System.SystemException ex)
            {
                //Logger.LogToOperations(ex, (int)EventId.ActivityError, EventSeverity.Error, LoggingAreaCategory);
                throw;
            }

        }

        private static string DecryptString(string encryptedString)
        {
            string str = encryptedString;

            if (encryptedString.StartsWith(Constants.EncryptedPasswordPrefix))
            {
                str = Helper.DecryptString(str.Replace(Constants.EncryptedPasswordPrefix, String.Empty));
            }

            return str;
        }

        private static bool IsSigningRequired
        {
            get
            {
                if (!SPFarm.Local.Properties.ContainsKey(Constants.ExecutePowerShellActivitySigningRequired))
                {
                    //Logger.LogToOperations("Defaulting to signing is required Farm property missing", (int)EventId.TraceMessage, EventSeverity.Warning, LoggingAreaCategory);
                    return true;
                }
                else
                {
                    return (bool)SPFarm.Local.Properties[Constants.ExecutePowerShellActivitySigningRequired];
                }

            }
        }

        private static void ValidateSignature(string script, string signature)
        {
            if (String.IsNullOrEmpty(signature) || !Helper.VerifySignature(script, signature))
            {
                throw new SecurityException("The script signature is not valid!");
            }
        }

        protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
        {
            Nintex.Workflow.Diagnostics.ActivityErrorHandler.HandleFault(
              executionContext,
              exception,
              this.WorkflowInstanceId,
              "Error executing PowerShell Script",
              __ListItem.Id,
              __ListId,
              __Context);

            return base.HandleFault(executionContext, exception);
        }
    }

}
